Skip to content

CI: require swift-testing-extensions 0.3.1 for WASM (groundwork for running tests) - #89

Closed
mansbernhardt wants to merge 1 commit into
orchetect:mainfrom
mansbernhardt:ci/wasm-run-tests
Closed

CI: require swift-testing-extensions 0.3.1 for WASM (groundwork for running tests)#89
mansbernhardt wants to merge 1 commit into
orchetect:mainfrom
mansbernhardt:ci/wasm-run-tests

Conversation

@mansbernhardt

@mansbernhardt mansbernhardt commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Groundwork for running swift test on the WASM jobs rather than only swift build. Not green yet — but the remaining blocker is no longer tooling, and is described below.

(Rewritten: this PR originally removed TestingExtensions from SwiftTimecodeCoreTests. With 0.3.1 that is unnecessary, so the removal is gone and this is now a one-line version bump.)

The two tooling blockers, both solved

1. SWCompression — fixed by 0.3.1. In 0.3.0, Algorithm+DEFLATE.swift imports it from the #if canImport(Darwin) / #else branch, so it fires on every non-Darwin platform including WASI. 0.3.1 narrows it to #if os(Linux), which is correct: on wasm32-unknown-wasip1, os(WASI) is true and os(Linux) is false (probed directly). This PR requires 0.3.1 so resolution is explicit rather than depending on what a consumer's Package.resolved happens to hold.

2. The macro errors — your prebuilds hunch was exactly right. The SwiftPM CLI equivalent of -IDEPackageEnablePrebuilts=NO is --disable-experimental-prebuilts:

swift build --swift-sdk swift-6.3-RELEASE_wasm --build-tests --disable-experimental-prebuilts

With it, both @Test"global variable must be a compile-time constant to use @section attribute" and @Suite"'@const' value should be initialized with a compile-time value" disappear.

What is left: 8 sites, one file, and it is an API question

With 0.3.1 + that flag, the entire suite compiles for wasm32 except 8 errors, all in Tests/SwiftTimecodeCoreTests/Timecode/Source/Timecode Samples Tests.swift — 4 integer literals and 4 DoubleInt conversions that overflow.

These are not test bugs. The API is:

public func samplesValue(sampleRate: Int) -> Int

and the tests use values like 4_147_200_000 — 24 hours at 48 kHz. On a 32-bit platform that is simply unrepresentable, so audio sample counts are a fourth total-count domain in the same family as frames, subframes and Fraction — and the one with the least headroom, since it overflows at ordinary limits rather than at a hypothetical future frame rate.

So this PR is gated on the consistency work discussed in #88, not on CI configuration. Once totals are Int64, adding the WASM test job here should be mechanical.

Notes for whoever wires the job up

Four things that cost me time on my own WASM test lane:

  1. swift build --build-tests has no per-target form, so it builds every test target including Apple-only ones. I narrow the manifest from an env var to the targets I want plus their transitive first-party deps.
  2. SwiftPM's generated runner defaults to XCTest, whose wasi entry point reads Bundle.main and traps before running anything. Pass --testing-library swift-testing.
  3. Run --no-parallel — a trap kills the process, and only serial execution makes "last test to start" the actual culprit.
  4. Give the module a real stack. wasi-libc links a 64 KiB stack and wasm32 has no guard page, so overflow silently corrupts the heap and surfaces later, differently each run. It must go in the manifest, not the CLI: a CLI -Xlinker -z -Xlinker stack-size=… also reaches the host link of the macro plugins, and macOS ld rejects it.

@orchetect orchetect self-assigned this Aug 11, 2026
@orchetect orchetect added the testing Related to automated unit testing label Aug 11, 2026
@orchetect

orchetect commented Aug 11, 2026

Copy link
Copy Markdown
Owner

While not implicitly necessary, this surfaces the need to get the TestingExtensions package building cross-platform on WASM. Otherwise this situation will repeat itself over other repositories I use it in as a dependency. I use the dependency to test logic in various packages so I will eventually need it to be functional on WASM. However, it's a bit of chicken or the egg because I was waiting for a solution to get not just builds happening on CI but also unit testing for WASM. So maybe I can use this a template to add WASM testing for TestingExtensions itself.

I'm adding a build job in orchetect/swift-testing-extensions#3, and once that goes green a unit test run can be added after.

@orchetect

Copy link
Copy Markdown
Owner

if you know offhand why this package resolves swift-testing differently

I'm not sure if this sheds any light, but in TestingExtension's CI pipeline I have to disable prebuilds otherwise there are compilation errors. I also recall that if you somehow intermix prebuilds and non-prebuilds in the derived data folder, it can really jam things up. Deleting DerivedData may be necessary to rule that part out. I'm not sure what would be different about WASM here but this may be a clue.

https://github.com/orchetect/swift-testing-extensions/blob/966023b48c4d74142f0233b304c3e4935f9a49b0/.github/workflows/build.yml#L71-L80

@orchetect

orchetect commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Why is the commit from #88 duplicated here? It's not necessary for building WASM CI tests to run (but not pass), which was why I suggested branching from main so it would be a totally separate concern.

@mansbernhardt

Copy link
Copy Markdown
Contributor Author

Apologies on the duplicated commit — that was my error, I branched from the #88 work rather than from main. Rebased; this is now a single commit touching only Package.swift.

Your prebuilds hunch was exactly right, and it is the whole blocker. The SwiftPM CLI equivalent of your -IDEPackageEnablePrebuilts=NO is --disable-experimental-prebuilts:

swift build --swift-sdk swift-6.3-RELEASE_wasm --build-tests --disable-experimental-prebuilts

With that flag the @Test / @section error disappears completely. So WASM unit testing is not blocked on anything structural — just on that flag.

Two real errors were hiding underneath it:

  1. Timecode Samples Tests.swift — integer literals like 4_147_200_000 stored into Int, plus several DoubleInt conversions that overflow. That is 48 kHz × 24 hours, so audio sample counts are a third total-count domain with the same 32-bit problem as frames and subframes — and the one with the least headroom, since it overflows at the library's ordinary limits rather than at some hypothetical future frame rate. Relevant to the consistency question you raised on Fix Int overflow on 32-bit platforms (wasm32, armv7) #88; I have noted it there.
  2. @Suite expansion'@const' value should be initialized with a compile-time value. Still swift-testing on WASM rather than anything in your packages.

To be clear about where this leaves things: I have moved it from one compile error to two different ones, not to a passing suite. But the diagnosis is solid and the prebuilts part is verified.

On TestingExtensions — I think you have already fixed it. In 0.3.0 (what this package resolves), Algorithm+DEFLATE.swift imports SWCompression inside the #if canImport(Darwin) / #else branch, so it fires on every non-Darwin platform including WASI — line 11, exactly where the build here failed. On current main you have narrowed it to #if os(Linux), which is correct: I probed wasm32-unknown-wasip1 directly and os(WASI) is true while os(Linux) is false.

So that may need only a release and a version bump rather than new work. Happy to test it here if you cut one — I can point this branch at it and report back.

@orchetect

Copy link
Copy Markdown
Owner

I pushed TestingExtensions 0.3.1 earlier today with a passing build on WASM. You're probably building off your local resolved file which would be behind. SwiftTimecode's manifest can be updated to that version so it forces it.

Groundwork for running `swift test` on the WASM jobs rather than only
`swift build`. Not green yet — see the PR description.

0.3.0 imports `SWCompression` from the `#if canImport(Darwin)` / `#else`
branch, so it fires on every non-Darwin platform including WASI, and
`SWCompression` has no WebAssembly support. 0.3.1 narrows it to `#if os(Linux)`,
which is correct — on wasm32-unknown-wasip1, `os(WASI)` is true and `os(Linux)`
is false.

Requiring 0.3.1 makes the resolution explicit rather than leaving it to whatever
a consumer's Package.resolved happens to hold.
@mansbernhardt mansbernhardt changed the title CI: groundwork for running the test suite on WASM (not green yet) CI: require swift-testing-extensions 0.3.1 for WASM (groundwork for running tests) Aug 12, 2026
@mansbernhardt

Copy link
Copy Markdown
Contributor Author

That was it — thank you. Retested with 0.3.1 and the dependency kept, and the tooling side is fully clear:

  • SWCompression — gone (0.3.1)
  • @Test / @section — gone (--disable-experimental-prebuilts)
  • @Suite / @const — gone (same flag)

So I have dropped the dependency removal entirely; this PR is now a one-line bump to from: "0.3.1", and the description is rewritten around what actually blocks it.

What remains is 8 errors, all in one fileTimecode Samples Tests.swift, 4 integer literals and 4 DoubleInt conversions. They are not test bugs:

public func samplesValue(sampleRate: Int) -> Int

with tests using 4_147_200_000 — 24 hours at 48 kHz, unrepresentable in a 32-bit Int. So audio sample counts are a fourth total-count domain alongside frames, subframes and Fraction, and the one with the least headroom: it overflows at the library's ordinary limits rather than at a hypothetical future frame rate.

Which means this PR is gated on the consistency work in #88 rather than on anything CI-side. Once totals are Int64, wiring up the actual job should be mechanical — I have put the four gotchas from my own WASM test lane in the PR description so they are not rediscovered.

Happy to do the samples part along with the rest whenever you have decided how you would like #88 staged.

@orchetect

orchetect commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Apologies on the duplicated commit

No worries, just wanted to point that out before more commits were added downstream here.

What remains is 8 errors, all in one file — Timecode Samples Tests.swift
this PR is gated on the consistency work in #88

Ok, that's great. If the refactors for audio sample rate are addressed in #88 then we can either 1) merge this PR now and open a new PR later to add the actual WASM CI test run job, or 2) keep this PR open and after merging #88 to main you can pull-up main into this PR and add the WASM CI test job here.

@orchetect
orchetect self-requested a review August 12, 2026 18:48
@mansbernhardt

Copy link
Copy Markdown
Contributor Author

Correction, and apologies — I got this wrong.

--disable-experimental-prebuilts does not fix the macro errors. I re-tested properly: same branch, clean scratch directory each time, only the flag differing.

--disable-experimental-prebuilts  →  2816 @section/@const errors
(no flag)                         →  2816 @section/@const errors

My earlier claim was an artifact of my own output filtering. Macro-expansion diagnostics carry no file:line: prefix, so the grep I was using to summarise errors never displayed them, and a sort -u | head truncated what was left. I read their absence from my terminal as their being fixed. They were there the whole time — which, given what you just said about chasing false positives, is exactly the kind of thing I did not want to hand you. Sorry.

So the remaining blocker for compiling tests on WASM is still @Test / @Suite expansion emitting @section globals and @const values that wasm32 rejects, and I do not know why. What I can say is that a working configuration exists — my own project compiles and runs 551 swift-testing tests on wasm32 under wasmtime — but I have not isolated what differs between the two, and I am not going to guess at it a second time.

The other half of the diagnosis stands, and that one I did verify directly: 0.3.0 imports SWCompression from the #if canImport(Darwin) / #else branch so it fires on WASI, 0.3.1 narrows it to #if os(Linux), and os(WASI) is true while os(Linux) is false on wasm32-unknown-wasip1. This PR's one-line bump to 0.3.1 is unaffected by the correction above.

@orchetect

orchetect commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Ok we can circle back to this after there is a chance to do more triage on the issue.

Just for clarification - Swift Testing itself does actually work in a WASM unit test run? As in, a brand new empty Swift package with a test target and one @Test method will compile and run? I think I inferred that from your local test run succeeding.

@orchetect

Copy link
Copy Markdown
Owner

With #88 merged down to main, do we want to pursue this further prior to a patch release?

My rationale is that if a WASM unit test reveals any holes that need patching up it would be better to have them all passing before a release.

@mansbernhardt

Copy link
Copy Markdown
Contributor Author

My view: no — don't hold a patch release for this.

What is left here is blocked on something neither of us controls. The 0.3.1 bump does its job (it forces a WASM-capable resolution rather than leaving it to whatever a consumer's Package.resolved happens to hold), but the actual CI test job still cannot run, because @Test / @Suite expansion emits @section globals and @const values that wasm32 rejects. That is a swift-testing-on-WASM issue, unrelated to your packages, and I have not isolated it. Holding a release for it means holding it on an unknown.

Whereas #88 is worth shipping on its own: it closes a trap that aborts all Timecode arithmetic on any 32-bit platform, and it is now on main with your fixups.

So my suggestion is: merge this as the one-line bump whenever convenient, cut the patch release off main, and let the WASM CI job come later as its own PR once the macro issue is understood. I am happy to keep chasing that separately.

Being transparent about the interest: we currently pin your fork branch by revision:, so a tagged patch release is what lets us move back onto a released version. That is a real motivation on my side and you should weigh it as such — but I think the argument holds regardless, since the 32-bit fix and the WASM CI job have no dependency on each other.

@orchetect

Copy link
Copy Markdown
Owner

Totally agree, in that case. I will push a patch release.

There is no rush for a WASM CI test run -- but if you do happen to work out the blocks at some point please let me know. The benefit would be broad however as I would then be able to implement it on multiple other repositories moving forward where I currently only have build jobs.

@orchetect

Copy link
Copy Markdown
Owner

As it so happens, I bumped the dependency version directly on main a few days ago while doing some maintenance (in e34ec85).

I'm fine to leave this PR open if you prefer to resume at some point in the future with the discussion history in-place. But it's fine if we just close it and open a new PR in future when/if needed.

@mansbernhardt

Copy link
Copy Markdown
Contributor Author

Confirmed — main already reads from: "0.3.1" from your maintenance commit, so this PR's entire diff is now a no-op. Closing it rather than leaving a stale PR on your queue.

For the record, so nothing is lost when it disappears: the two blockers for actually running the test suite on WASM are

  1. @Test / @Suite expansion emitting @section globals and @const values that wasm32 rejects — not fixed by --disable-experimental-prebuilts, contrary to what I claimed earlier and then corrected
  2. 373 instances of integer literal '1234567891234564567' overflows when stored into 'Int', from oversized numeric strings in the string-parsing and FeetAndFrames tests — present on a pristine main, so independent of anything in Fix Int overflow on 32-bit platforms (wasm32, armv7) #88

Plus the four practical notes from my own WASM test lane, in case they save you time when you get to it: --build-tests has no per-target form so it builds every target including Apple-only ones; SwiftPM's runner defaults to XCTest, whose wasi entry point reads Bundle.main and traps before running anything, so --testing-library swift-testing is required; --no-parallel is what makes "last test to start" the actual culprit when something traps; and wasi-libc's 64 KiB stack with no guard page needs raising in the manifest, not on the command line, because a CLI -Xlinker also reaches the host link of the macro plugins and macOS ld rejects it.

Happy to open a fresh PR for the CI job once the macro issue is understood. Thanks for the review throughout — the PlatformInt shape is better than either of the two I brought you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testing Related to automated unit testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants